1. webpack 构建流程是什么?
- 初始化参数: 解析 webpack 配置参数,合并 shell 传入和 webpack.config.js 文件配置的参数,形成最后的配置结果;
- 开始编译:上一步得到的参数初始化 compiler 对象,注册所有配置的插件,插件监听 webpack 构建生命周期的事件节点,做出相应的反应,执行对象的 run 方法开始执行编译;
- 确定入口:从配置的 entry 入口,开始解析文件构建 AST 语法树,找出依赖,递归下去;
- 编译模块:递归中根据文件类型和 loader 配置,调用所有配置的 loader 对文件进行转换,再找出该模块依赖的模块,再递归本步骤知道找出入口依赖的文件都经过本步骤的处理;
- 完成模块编译并输出,递归完事后,得到每个文件结果,包含每个模块以及他们之间的依赖关系,根据 entry 或分包配置生成代码块 chunk;
- 输出完成:输出所有 chunk 到文件系统;
2. webpack 热更新原理
其实是自己开启了 express 应用,添加了对 webpack 编译的监听,添加了和浏览器的 websocket 长连接,当文件变化触发 webpack 进行编译完成后,会通过 socket 消息告诉浏览器准备刷新。而为了减少刷新的代价,就是不用刷新网页,而是刷新某个模块,webpack-dev-server 可以支持热更新,通过生成文件的 hash 值来对比需要更新的模块,浏览器再进行热替换
服务端
- 启动 webpack-dev-server 服务器
- 创建 webpack 实例
- 创建 server 服务器
- 添加 webpack 的 done 事件回调
- 编译完成向客户端发送消息
- 创建 express 应用 app
- 设置文件系统为内存文件系统
- 添加 webpack-dev-middleware 中间件
- 中间件负责返回生成的文件
- 启动 webpack 编译
- 创建 http 服务器并启动服务
- 使用 sockjs 在浏览器端和服务器端之间建立一个 websocket 长连接
- 创建 socket 服务器
客户端
- webpack-dev-server/client 端会监听到 hash 消息
- 客户端收到的 ok 消息后会执行 reloadApp 方法进行更新
- 在 reloadApp 中会进行判断,是否支持热更新,如果支持的话发生 webpackHotUpdate 事件,如果不支持就直接刷新浏览器
- 在 webpack/hot/dev-server.js 会监听 webpackHotUpdate 事件
- 在 check 方法里会调用 module.hot.check 方法
- HotModuleReplacement.runtime 请求 manifest
- 通过调用 JsonpMainTemplate.runtime 的 hotDownloadUpdateChunk 方法通过 JSONP 请求获取最新的模块代码
- 补丁 js 取回来会调用 JsonpMainTemplate.runtime 的 webpackHotUpdate 方法,
- 然后会调用 HotModuleReplacement.runtime.js 的 hotAddUpdateChunk 方法动态更新模块代码
- 然后调用 hotApply 热更新
3. webpack 打包的 hash 码是如何生成的?
webpack 中存在多种 hash:
- hash: 每次 webpack 编译中生成的 hash 值,一次打包,只有一个 hash 值,每次构建生成新的 hash。
- chunkhash: 每个 chunk 打包后,会产生的哈希,叫做 chunkhash
- contenthash: 根据文件内容创建,当文件内容发生变化时,contenthash 发生变化。
hash、chunkhash、contenthash,首先生成效率越来越低,成本越来越高,影响范围越来越小,精度越来越细。
hash是一整个项目,一次打包,只有一个hash值,是项目级的
chunhash是从入口entry出发,到它的依赖,以及依赖的依赖,依赖的依赖的依赖,等等,一直下去,所打包构成的代码块(模块的集合)叫做一个chunk,也就是说,入口文件和它的依赖的模块构成的一个代码块,被称为一个chunk。
contenthash是哈希只跟内容有关系,内容不变,哈希值不变。与chunkhash的区别可以举上面contenthash的例子,同时可以说明contenthash跟内容有关,但是chunkhash会考虑很多因素,比如模块路径、模块名称、模块大小、模块id等等。
text
什么是chunk?
- 从入口entry出发,到它的依赖,以及依赖的依赖,依赖的依赖的依赖,等等,一直下去,所打包构成的代码块(模块- 的集合)叫做一个chunk,也就是说,入口文件和它的依赖的模块构成的一个代码块,被称为一个chunk。
- 所以,一个入口对应一个chunk,多个入口,就会产生多个chunk
- 所以,单入口文件,打包后chunkhash和hash值是不同的,但是效果是一样的js
// webpack.config.js
module.exports = {
entry: {
entry1: './src/entry1.js',
entry2: './src/entry2.js',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'bundle.[chunkhash:8].js' //此处应该用chunkhash,因为有两个入口文件,如果用 hash 输出的文件名就一样了。
},
}4. webpack 如何利用 localStorage 离线缓存静态资源?
- 在配置 webpack 时,我们可使用html-webpack-plugin来注入一段脚本到 html, 来实现第三方或者公用资源的静态化存储。做法:在 html 中注入一段标识,例如<% HtmlWebpackPlugin.options.loading.html %>,在html-webpack-plugin中即可通过配置 html 属性,将 script 注入进去。
- 通过配置webpack-mainfest-plugin,生成 mainfest.json 文件用来对比 js 资源的差异,做到是否替换,当然也要写缓存 script。
- 在我们做 CI/CD 的时候,也可以通过编辑文件流来实现静态化脚本的注入,来降低服务器的压力,提高性能。
- 可以通过自定义 plugin 或者html-webpack-plugin等周期函数,动态注入前端代码,动态化存储 script。
5. webpack 常见的 plugin有哪些
- ProvidePlugin: 自动加载模块,代替 require 和 import
- html-webpack-plugin 可以根据模板自动生成 html 代码,并自动引用 css 和 js 文件
- extract-text-webpack-plugin 将js文件中引用的样式单独抽离成css文件
- DefinePlugin 编译时配置全局变量,这对开发模式和发布模式的构建允许不同的行为非常有用
- HotModule ReplacementPlugin 热更新
- optimize-css-assets-webpack-plugin 不同组件中重复的Css可以快速去重
- webpack-bundle-analyzer 一个webpak的bundle 文件分析工具,将bundle文件可交互缩放的treemap的形式展示。
- compression-webpack-plugin 生产环境下采用gzip 压缩JS和CSS
- happypack:通过多进程模型,来加速代码构建
- clean-wenpack-plugin 清理每次打包下没有使用的文件
webpack 插件如何实现
6. Tree Shaking 与 sideEffects
- Tree Shaking 基于 ES Module 的静态分析,只会移除“未被引用”的导出,不会分析函数是否有副作用;因此库作者需要在
package.json标记"sideEffects": false | string[],告诉 webpack 某些文件不能删。 - 如果项目里有 CSS/Polyfill 等“导入即执行”的模块,要么把它们列在
"sideEffects"中,要么改为在业务代码里显式调用函数返回值,否则生产构建可能会被摇掉。 - Babel 转译可能把
import转成require,导致失去 Tree Shaking 能力;需要确保@babel/preset-env的modules: false,交给 webpack 去处理模块语法。